home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
src
/
fmsdisk
/
fms.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-09
|
11KB
|
527 lines
/*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
/*
* FMS.C
*
* File Disk Device (fmsdisk.device)
*
* Simulates a trackdisk by using a large file to hold the 'blocks'.
*/
#include "defs.h"
Prototype DevCall NDev *DevInit(register __a0 APTR);
Prototype DevCall NDev *DevOpen(register __d0 long, register __a1 IOB *, register __d1 long);
Prototype APTR DevExpunge(void);
Prototype APTR DevClose(register __a1 IOB *);
Prototype APTR DevReserved(void);
Prototype void DevBeginIO(register __a1 IOB *iob);
Prototype void DevAbortIO(register __a1 IOB *iob);
Prototype void SynchroMsg(UWORD, NDUnit *);
Prototype __geta4 void CoProc(void);
Prototype void ExtendSize(NDUnit *, long);
Prototype void FlushCache(void);
Prototype void GetUnitName(int, char *);
extern struct Library *SysBase;
struct DosLibrary *DOSBase = NULL;
NDev *DevBase;
APTR DevSegment;
PORT *FProc;
PORT FPort;
#ifdef DEBUG
long DbFH;
#endif
NDUnit *CacheUnit;
long CacheLen;
char CacheBuf[32768]; /* 32K buffer */
/*
* Init, segment in A0 (registered args), arg must be declared as a pointer
*/
DevCall
NDev *
DevInit(register __a0 APTR seg)
{
static func_ptr DevVectors[] = {
(func_ptr)DevOpen,
(func_ptr)DevClose,
(func_ptr)DevExpunge,
(func_ptr)DevReserved,
(func_ptr)DevBeginIO,
(func_ptr)DevAbortIO,
(func_ptr)-1
};
NDev *db;
DevBase = db = (NDev *)MakeLibrary((long **)DevVectors,NULL,NULL,sizeof(NDev),NULL);
db->Lib.lib_Node.ln_Type = NT_DEVICE;
db->Lib.lib_Node.ln_Name = DeviceName;
db->Lib.lib_Flags = LIBF_CHANGED|LIBF_SUMUSED;
db->Lib.lib_Version = 1;
db->Lib.lib_IdString= (APTR)IdString;
DevSegment = seg;
AddDevice((DEV *)db);
return(db);
}
/*
* open device
*
* unitnum: D0
* iob: A1
* flags: D1
*
* have dummy pointer before iob so iob gets assigned A1 instead of A0
* for registered args interface.
*/
DevCall
NDev *
DevOpen(register __d0 long unitnum,
register __a1 IOB *iob,
register __d1 long flags
)
{
NDev *nd = DevBase;
NDUnit *unit = &nd->Unit[unitnum];
if (++nd->Lib.lib_OpenCnt == 1) {
FProc = CreateProc("FMS-Dummy", 0, (long)DUMmySeg >> 2, 8192);
FPort.mp_SigBit = SIGBREAKB_CTRL_D; /* port init */
FPort.mp_SigTask= FProc->mp_SigTask;
FPort.mp_Flags = PA_SIGNAL;
NewList(&FPort.mp_MsgList);
}
if (++unit->OpenCnt == 1)
SynchroMsg(CMD_OPENUNIT, unit);
nd->Lib.lib_Flags &= ~LIBF_DELEXP;
iob->io_Unit = (struct Unit *)unit;
iob->io_Error = 0;
return(nd);
}
/*
* expunge device, no arguments
*/
APTR
DevExpunge(void)
{
NDev *nd = DevBase;
APTR dseg = DevSegment;
if (dseg == NULL)
Alert(24);
if (nd->Lib.lib_OpenCnt) {
nd->Lib.lib_Flags |= LIBF_DELEXP;
return(NULL);
}
Remove((NODE *)nd);
FreeMem((char *)nd - nd->Lib.lib_NegSize, nd->Lib.lib_NegSize + nd->Lib.lib_PosSize);
ADevExpunge();
return(dseg);
}
/*
* device close. Dummy pointer before iob so iob gets assigned A1
*
* iob: A1
*/
DevCall
APTR
DevClose(register __a1 IOB *iob)
{
NDev *nd = DevBase;
{
NDUnit *unit = (NDUnit *)iob->io_Unit;
if (unit->OpenCnt && --unit->OpenCnt == 0)
SynchroMsg(CMD_CLOSEUNIT, unit);
}
if (nd->Lib.lib_OpenCnt && --nd->Lib.lib_OpenCnt)
return(NULL);
if (FProc) {
SynchroMsg(CMD_KILLPROC, NULL);
FProc = NULL;
}
if (nd->Lib.lib_Flags & LIBF_DELEXP)
return(DevExpunge());
/*
* close down resources
*/
return(NULL);
}
DevCall
APTR
DevReserved(void)
{
return((APTR)0);
}
DevCall
void
DevBeginIO(register __a1 IOB *iob)
{
/*NDev *nd = DevBase;*/
iob->io_Error = 0;
iob->io_Actual = 0;
switch(iob->io_Command & ~TDF_EXTCOM) {
case CMD_INVALID:
iob->io_Error = IOERR_NOCMD;
break;
case CMD_RESET:
break;
case CMD_READ:
PutMsg(&FPort, &iob->io_Message);
iob->io_Flags &= ~IOF_QUICK; /* not quick */
iob = NULL;
break;
case CMD_WRITE:
PutMsg(&FPort, &iob->io_Message);
iob->io_Flags &= ~IOF_QUICK; /* not quick */
iob = NULL;
break;
case CMD_UPDATE:
PutMsg(&FPort, &iob->io_Message);
iob->io_Flags &= ~IOF_QUICK; /* not quick */
iob = NULL;
break;
case CMD_CLEAR:
break;
case CMD_STOP:
break;
case CMD_START:
break;
case CMD_FLUSH:
break;
case TD_MOTOR: /* motor, no action */
case TD_SEEK: /* seek, no action */
break;
case TD_FORMAT: /* format */
PutMsg(&FPort, &iob->io_Message);
iob->io_Flags &= ~IOF_QUICK; /* not quick */
iob = NULL;
break;
case TD_REMOVE: /* not supported */
iob->io_Error = IOERR_NOCMD;
break;
case TD_CHANGENUM: /* change count never changes */
iob->io_Actual = 1;
break;
case TD_CHANGESTATE: /* 0=disk in drive */
iob->io_Actual = 0;
break;
case TD_PROTSTATUS: /* io_Actual -> 0 (rw) */
iob->io_Actual = 0;
break;
case TD_RAWREAD: /* not supported */
case TD_RAWWRITE:
iob->io_Error = IOERR_NOCMD;
break;
case TD_GETDRIVETYPE: /* drive type? */
iob->io_Actual = 0;
break;
case TD_GETNUMTRACKS:
iob->io_Actual = 0; /* # of tracks? */
break;
case TD_ADDCHANGEINT: /* action never taken */
case TD_REMCHANGEINT:
break;
default:
iob->io_Error = IOERR_NOCMD;
break;
}
if (iob) {
if ((iob->io_Flags & IOF_QUICK) == 0)
ReplyMsg((MSG *)iob);
}
}
DevCall
void
DevAbortIO(register __a1 IOB *iob)
{
}
/*
* Server communications. If we run out of memory, well, we retry
* until memory is available.
*/
void
SynchroMsg(cmd, unit)
UWORD cmd;
NDUnit *unit;
{
IOB Iob;
do {
Iob.io_Message.mn_ReplyPort = CreatePort(NULL, 0);
Iob.io_Command = cmd;
Iob.io_Unit = (struct Unit *)unit;
} while (Iob.io_Message.mn_ReplyPort == NULL);
PutMsg(&FPort, &Iob.io_Message);
WaitPort(Iob.io_Message.mn_ReplyPort);
DeletePort(Iob.io_Message.mn_ReplyPort);
}
/*
* SERVER SIDE (IS A PROCESS)
*
* File name is:
*/
__geta4 void
CoProc(void)
{
IOB *iob;
NDUnit *unit;
char buf[128];
char notdone = 1;
Wait(SIGBREAKF_CTRL_D); /* wait for port init */
while (notdone) {
WaitPort(&FPort);
while (iob = (IOB *)GetMsg(&FPort)) {
unit = (NDUnit *)iob->io_Unit;
#ifdef DEBUG
if (DbFH) {
sprintf(buf, "Cmd %08lx/%04x @ %08lx Buf %08lx %04x\n",
unit, iob->io_Command, iob->io_Offset, iob->io_Data, iob->io_Length
);
Write(DbFH, buf, strlen(buf));
}
#endif
/*
* cache (increase OFS throughput)
*/
if (CacheLen && (iob->io_Command & ~TDF_EXTCOM) != CMD_WRITE)
FlushCache();
switch(iob->io_Command & ~TDF_EXTCOM) {
case CMD_OPENUNIT:
GetUnitName(unit - &DevBase->Unit[0], buf);
unit->Fh = Open(buf, 1005);
if (unit->Fh == NULL) {
unit->Fh = Open(buf, 1006);
unit->Extended = 1;
}
#ifdef DEBUG
if (DbFH) {
Write(DbFH, "OPEN ", 5);
Write(DbFH, buf, strlen(buf));
Write(DbFH, "\n", 1);
}
#endif
if (unit->Fh) {
Seek(unit->Fh, 0L, 1);
unit->Size = Seek(unit->Fh, 0L, -1);
}
unit->Pos = -1;
break;
case CMD_CLOSEUNIT:
if (unit->Fh) {
Close(unit->Fh);
unit->Fh = NULL;
}
break;
case CMD_KILLPROC:
notdone = 0;
break;
case CMD_READ:
if (unit->Fh == NULL)
break;
if (iob->io_Offset + iob->io_Length > unit->Size)
ExtendSize(unit, iob->io_Offset + iob->io_Length);
if (unit->Pos != iob->io_Offset)
Seek(unit->Fh, iob->io_Offset, -1);
iob->io_Actual = Read(unit->Fh, (char *)iob->io_Data, iob->io_Length);
if (iob->io_Actual == iob->io_Length)
unit->Pos = iob->io_Offset + iob->io_Actual;
else
unit->Pos = -1;
break;
case CMD_WRITE:
/*
* This causes file to be closed/reopened after
* formatting.
*/
if (unit->Extended && unit->Fh) {
FlushCache();
Close(unit->Fh);
GetUnitName(unit - &DevBase->Unit[0], buf);
unit->Fh = Open(buf, 1005);
unit->Extended = 0;
}
/* fall through */
case TD_FORMAT:
if (unit->Fh == NULL)
break;
if (iob->io_Offset > unit->Size) {
FlushCache();
ExtendSize(unit, iob->io_Offset);
}
if (CacheUnit != unit) {
FlushCache();
CacheUnit = unit;
}
if (unit->Pos != iob->io_Offset) {
/*
* Handle case where a CMD_WRITE modifies a previously
* cached write (occurs with OFS *a lot*)
*/
if (CacheLen) {
long ci = CacheLen - (unit->Pos - iob->io_Offset);
if (ci >= 0 && ci + iob->io_Length <= CacheLen) {
#ifdef DEBUG
if (DbFH)
Write(DbFH, "XBACK\n", 5);
#endif
iob->io_Actual = iob->io_Length;
movmem(iob->io_Data, CacheBuf + ci, iob->io_Length);
break;
}
}
FlushCache();
Seek(unit->Fh, iob->io_Offset, -1);
}
if (CacheLen + iob->io_Length > sizeof(CacheBuf))
FlushCache();
if (CacheLen + iob->io_Length <= sizeof(CacheBuf)) {
iob->io_Actual = iob->io_Length;
unit->Pos = iob->io_Offset + iob->io_Actual;
movmem(iob->io_Data, CacheBuf + CacheLen, iob->io_Actual);
CacheLen += iob->io_Actual;
} else {
if (CacheLen)
FlushCache();
iob->io_Actual = Write(unit->Fh, (char *)iob->io_Data, iob->io_Length);
if (iob->io_Actual == iob->io_Length)
unit->Pos = iob->io_Offset + iob->io_Actual;
else
unit->Pos = -1;
}
break;
default:
break;
}
if (notdone == 0) /* forbid before falling through */
Forbid(); /* and esp before replying */
ReplyMsg(&iob->io_Message);
}
#ifdef DEBUG
if (DbFH == NULL)
DbFH = Open("con:0/0/320/100/Debug", 1006);
#endif
}
#ifdef DEBUG
if (DbFH)
Close(DbFH);
#endif
/* fall through to exit */
}
void
GetUnitName(int unitnum, char *buf)
{
BPTR envfile;
LONG envlen;
BOOL gotit = FALSE;
/* New - see if ENVIRONMENT variable FMSUnit<n> exists (ex. FMSUnit2)
* containing full path to file.
*/
sprintf(buf,"ENV:FMSUnit%d",unitnum);
if(envfile = Open(buf,MODE_OLDFILE))
{
envlen = Read(envfile,buf,128-2);
if(envlen >= 0)
{
gotit = TRUE;
buf[envlen] = '\0';
if(buf[envlen-1]=='\n') buf[envlen-1] = '\n';
}
Close(envfile);
}
/* Fallback to old FMS:Unit<n> name */
if(!gotit)
{
sprintf(buf, "FMS:Unit%d", unitnum);
}
}
/*
* Extend the file size in 4K chunks
*/
void
ExtendSize(unit, offset)
NDUnit *unit;
long offset;
{
long pos;
char *buf = AllocMem(EXT_CHUNK, MEMF_CLEAR|MEMF_PUBLIC);
if (buf) {
if (unit->Extended == 0)
unit->Extended = 1;
Seek(unit->Fh, 0L, 1);
pos = Seek(unit->Fh, 0L, 0);
while (pos < offset) {
if (Write(unit->Fh, buf, EXT_CHUNK) != EXT_CHUNK)
break;
pos += EXT_CHUNK;
}
FreeMem(buf, EXT_CHUNK);
unit->Pos = -1; /* unknown */
}
}
/*
* flush sequential write cache
*/
void
FlushCache()
{
NDUnit *unit = CacheUnit;
#ifdef DEBUG
if (DbFH)
Write(DbFH, "FLUSH\n", 6);
#endif
if (CacheLen) {
Write(unit->Fh, CacheBuf, CacheLen);
CacheLen = 0;
}
}